
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 *
 */
public class TCPClient {
    private String        ip;
    private int           port          = 18080;
    private static int    MAX_PACK_SIZE = 1024 * 64;          // 最大包长64K
    private AtomicInteger seqID         = new AtomicInteger();
    final static int      TIMEOUT       = 1000 * 10;

    public TCPClient(String ip, int port) {
        this.ip = ip;
        this.port = port;
    }

    private Socket socket = null;

    public synchronized void connect() throws IOException {
        if (socket != null) {
            return;

        }
        socket = new Socket();
        InetSocketAddress addr = new InetSocketAddress(ip, port);
        try {
            socket.setSoTimeout(TIMEOUT);
            socket.connect(addr, TIMEOUT);
        } catch (IOException e) {
            e.printStackTrace();
            socket = null;
            throw e;
        }
    }

    DataOutputStream dataOutputStream;
    DataInputStream  dataInputStream;
    private boolean  isRunning = true;

    public synchronized DataOutputStream getOutPut() {
        if (socket == null) {
            throw new IllegalStateException("socket not connect");
        }
        try {
            if (dataOutputStream == null) {
                dataOutputStream = new DataOutputStream(socket.getOutputStream());
            }
            return dataOutputStream;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    public synchronized DataInputStream getInput() {
        if (socket == null) {
            throw new IllegalStateException("socket not connect");
        }
        try {
            if (dataInputStream == null) {
                dataInputStream = new DataInputStream(socket.getInputStream());
            }
            return dataInputStream;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 根据帧头4字节为帧长度的协议读取帧长度
     * 
     * @param is
     * @return
     * @throws IOException
     */
    static ByteBuffer readFrameLen(InputStream is) throws IOException {
        // read packLen
        byte[] bs = new byte[4];
        IOUtil.readAll(is, bs, 0, 4);
        int packLen = ((bs[0] & 0xff) << 24) | ((bs[1] & 0xff) << 16) | ((bs[2] & 0xff) << 8) | ((bs[3] & 0xff) << 0);

        // check packLen
        if (packLen < 0 || packLen > MAX_PACK_SIZE) {
            throw new IOException("PackLen error:" + packLen);
        }
        // System.out.println("packLen = "+packLen);

        // read data
        ByteBuffer buf = ByteBuffer.allocate(packLen);
        buf.putInt(packLen);
        IOUtil.readAll(is, buf.array(), 4, packLen);
        buf.rewind();// very
        return buf;
    }

    public void sendOnly(byte[] request, int timeout) throws IOException {
        Socket socket = null;
        OutputStream output = null;
        try {
            socket = new Socket();
            InetSocketAddress addr = new InetSocketAddress(ip, port);
            socket.connect(addr, timeout);
            socket.setSoTimeout(timeout);
            output = socket.getOutputStream();

            // send
            int toServerSize = 8 + request.length;
            ByteBuffer buf = ByteBuffer.allocate(toServerSize);
            int seq = getNextSeq();
            buf.putInt(toServerSize).putInt(seq).put(request);
            byte[] content = buf.array();
            output.write(content, 0, toServerSize);
            output.flush();
        } finally {
            try {
                if (output != null)
                    output.close();
            } catch (Exception e) {
                // do nothing
                e.printStackTrace();
            }
            try {
                if (socket != null)
                    socket.close();
            } catch (Exception e) {
                // do nothing
                e.printStackTrace();
            }
        }
    }

    private int getNextSeq() {
        int i = seqID.getAndIncrement();
        return i & Integer.MAX_VALUE;
    }

    private void setupHeartBeatThread(final int interval) {
        new Thread() {
            public void run() {
                try {
                    int i = 0;
                    while (isRunning) {
                        synchronized (socket) {
                            if (socket.isConnected()) {
                                sendHelloMsg();
                                System.out.println("send:" + i + "|" + System.currentTimeMillis());
                                Thread.sleep(interval);
                                i++;
                            }
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            };
        }.start();

    }

    private void setTcpNoDelay(boolean b) {
        try {
            socket.setTcpNoDelay(b);
        } catch (SocketException e) {
            e.printStackTrace();
        }

    }

    private void sendMsg(byte[] array) {

        // send
        try {
            OutputStream output = socket.getOutputStream();
            output.write(array, 0, array.length);
            output.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void sendHelloMsg() {
        byte[] buf = new byte[37];
        buf[0] = 37;
        char[] hello = new char[] { 'h', 'e', 'l', 'l', '0' };
        for (int j = 0; j < hello.length; j++) {
            buf[32 + j] = (byte) hello[j];
        }
        sendMsg(buf);
    }

    public static void main(String[] args) throws IOException {
        ByteBuffer buf = ByteBuffer.allocate(5);
        for (int i = 0; i < 5; i++) {
            buf.put((byte) i);
        }
        TCPClient client = new TCPClient("122.224.212.103", 8080);
        try {
            client.connect();
            client.setTcpNoDelay(true);
            // client.setupHeartBeatThread(TIMEOUT / 2);
            client.sendHelloMsg();
            byte[] bytes = new byte[1024];
            while (client.socket.isConnected()) {
                if (IOUtil.read(client.socket.getInputStream(), bytes) > 0) {
                    System.out.println(System.currentTimeMillis() + "|recv data[0]:" + bytes[0] + "(" + client.socket
                        + ")");
                } else {

                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //
        // byte[] result = client.sendAndReceive(buf.array(), TIMEOUT);
        // System.out.println("receive:" + Arrays.toString(result));
        // result = client.sendAndReceive(buf.array(), TIMEOUT);
        // System.out.println("receive2:" + Arrays.toString(result));
        // client.sendOnly(buf.array());
    }

}
